﻿namespace EFS.RC.CC.Presences
{
	/// <summary>
	/// End point presence
	/// </summary>
	internal abstract class Application<EndPointDeployment, EndPointApplication> : EFS.RC.CC.Presences.Deployment<EndPointDeployment>
		where EndPointDeployment : EFS.RC.CC.Deployment.Entities.EndPoint
		where EndPointApplication : EFS.RC.CC.Applications.EndPoints.EndPoint<EndPointDeployment>
	{
		#region Private components

		// synk access
		private object syncAccess = new object();

		/// Application
		private EndPointApplication endPointApplication = null;

		#endregion

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="endPointsPresencesRegistrar"></param>
		/// <param name="endPointDeployment"></param>
		public Application(EFS.RC.CC.Presences.EndPoints.Registrar endPointsPresencesRegistrar, EndPointDeployment endPointDeployment)
			: base(endPointsPresencesRegistrar, endPointDeployment)
		{
		}

		/// Application
		public System.Boolean TryGetPresenceEndPointApplication(ref EndPointApplication refEndPointapplication)
		{
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				if (null != this.endPointApplication)
				{
					// set ref
					refEndPointapplication = this.endPointApplication;

					// ok
					return true;
				}

				// not set
				return false;
			}
		}

		/// Application
		public EndPointApplication PresenceEndPointApplication
		{
			get
			{
				// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
				{
					// check if we have an application
					EFS.Common.Verify.Reference(endPointApplication, "Application EndPoint not Created for this presence");

					// ok
					return endPointApplication;
				}
			}
		}

		/// <summary>
		/// Stop application end point
		/// </summary>
		/// <returns></returns>
		public EFS.Common.Async.AsyncProcess TryStopApplicationEndPoint()
		{
			// lock access
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check if started
				if (null != endPointApplication)
				{
					// shut down
					endPointApplication.BeginShutdown();
				}
			}

			return null;
		}

		/// <summary>
		/// Start end point application
		/// </summary>
		/// <returns></returns>
		public EFS.Common.Async.AsyncProcess StartApplicationEndPoint(Microsoft.Rtc.Collaboration.ApplicationEndpointSettings applicationEndpointSettings)
		{
			// log
			EFS.EventLog.Log.LogInfo(System.String.Format("Starting Application End Point {0}", applicationEndpointSettings.OwnerDisplayName), this);

			// SAL
			EFS.RC.CC.Tools.Loggers.SAL.Add(null, "StartApplicationEndPoint", applicationEndpointSettings.OwnerUri, this); 

			// lock access
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check if note already started
				if (null != endPointApplication)
				{
					throw new System.InvalidOperationException("EndPoint Applicationalready started");
				}
				
				// instance
				endPointApplication = InstanceEndPointApplication(applicationEndpointSettings);

				// check
				EFS.Common.Verify.Reference(endPointApplication, "endPointApplication");

				// register for shut down
				endPointApplication.AsinkTerminatedDelegate.Register(OnApplicationEndPointTerminated);
			}

			// start
			return endPointApplication.BeginStartup();
		}

		/// <summary>
		/// Agent session terminated
		/// </summary>
		private void OnApplicationEndPointTerminated(EFS.RC.CC.Applications.EndPoints.EndPoint<EndPointDeployment> endPointApplication)
		{
			// lock access
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check if this is our session
				if (null != this.endPointApplication && !this.endPointApplication.Equals(endPointApplication))
				{
					throw new System.InvalidOperationException("Different Application End Point received for terminated");
				}

				// remove end point
				this.endPointApplication = null;
			}
		}

		/// <summary>
		/// Create application instance
		/// </summary>
		/// <param name="applicationEndpointSettings"></param>
		/// <returns></returns>
		protected abstract EndPointApplication InstanceEndPointApplication(Microsoft.Rtc.Collaboration.ApplicationEndpointSettings applicationEndpointSettings);
	}
}
